{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 定义"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "假设文本字符串由字符集  $\\Sigma$  中的字符组成，字符集大小为  $|\\Sigma|$ （例如，对于英文字母和数字， $|\\Sigma| = 36$ ）。\n",
    "\n",
    "金字塔被划分为  $L$  层，每层将字符串划分为  $l$  个区域。第  $l$  层的直方图维度为  $l \\times |\\Sigma|$ 。\n",
    "\n",
    "**数学形式化定义**：\n",
    "\n",
    "设字符串  $s$  的长度为  $n$ ，第  $l$  层将字符串划分为  $l$  个区域，每个区域的长度为  $\\frac{n}{l}$ （假设  $n$  能被  $l$  整除，实际情况下可能需要近似处理或填充）。\n",
    "\n",
    "对于字符串 $s$ 和金字塔的第  $l$ 层，PHOC 的第  $(l, k, c)$ 维度（其中  $k$ 是区域索引， $c$ 是字符索引）计算为：\n",
    "\n",
    "$\\text{PHOC}_{l, k, c}(s) =    \\begin{cases}    1, & \\text{如果字符 } c \\text{ 出现在区域 } k \\text{ 中} \\\\    0, & \\text{否则}    \\end{cases}$\n",
    "\n",
    "其中，字符  $c$  是否出现在区域  $k$  中的判断基于字符在字符串中的位置。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "为了简化，我们假设字符串长度可以被区域数整除，且不考虑填充和截断。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_phoc(s, layers):\n",
    "    charset = set('abcdefghijklmnopqrstuvwxyz0123456789')\n",
    "    charset_size = len(charset)\n",
    "\n",
    "    # Initialize PHOC array\n",
    "    phoc = np.zeros((sum(layers * charset_size for layers in layers),), dtype=np.int8)\n",
    "\n",
    "    # String length\n",
    "    n = len(s)\n",
    "\n",
    "    # Calculate and fill PHOC\n",
    "    index = 0\n",
    "    for l in layers:\n",
    "        region_size = n // l\n",
    "        for k in range(l):\n",
    "            start = k * region_size\n",
    "            end = (k + 1) * region_size if (k + 1) * region_size <= n else n\n",
    "            region = s[start:end].lower()  # Convert to lowercase for uniform handling\n",
    "            print(f\"Layer {l} region {k+1}: '{region}'\")\n",
    "            row = []\n",
    "            for c in charset:\n",
    "                # Check if character c is in the current region\n",
    "                if c in region:\n",
    "                    phoc[index] = 1\n",
    "                row.append(f\"{c}: {phoc[index]}\")\n",
    "                index += 1\n",
    "            print(\"  \".join(row))\n",
    "\n",
    "    return phoc\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Layer 2 region 1: 'he'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 1  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 1  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 2 region 2: 'll'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 1  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 3 region 1: 'h'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 1  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 3 region 2: 'e'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 1  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 3 region 3: 'l'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 1  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 4 region 1: 'h'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 1  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 4 region 2: 'e'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 1  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 4 region 3: 'l'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 1  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 4 region 4: 'l'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 1  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 5 region 1: 'h'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 1  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 5 region 2: 'e'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 1  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 5 region 3: 'l'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 1  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 5 region 4: 'l'\n",
      "o: 0  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 1  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n",
      "Layer 5 region 5: 'o'\n",
      "o: 1  q: 0  a: 0  2: 0  1: 0  h: 0  s: 0  4: 0  r: 0  c: 0  0: 0  b: 0  w: 0  7: 0  u: 0  e: 0  x: 0  y: 0  n: 0  9: 0  z: 0  k: 0  6: 0  t: 0  v: 0  i: 0  l: 0  5: 0  m: 0  j: 0  3: 0  8: 0  f: 0  p: 0  d: 0  g: 0\n"
     ]
    }
   ],
   "source": [
    "# 示例\n",
    "layers = [2, 3, 4, 5]  # 金字塔层数和每层区域数\n",
    "s = \"hello\"\n",
    "phoc_repr = calculate_phoc(s, layers)\n",
    "# print(phoc_repr)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(504,)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "phoc_repr.shape"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**注意**：\n",
    "\n",
    "这个实现简单明了，没有处理字符串长度不能被区域数整除的情况。在实际应用中，可能需要对字符串进行填充或截断处理。\n",
    "\n",
    "我们假设所有字符均为小写以简化处理。如果需要区分大小写，可以将字符集 `charset` 相应地进行调整，并在处理时保留原始大小写。\n",
    "\n",
    "这个实现没有使用到 `sigma` 参数，但保留了它以兼容未来可能的扩展，例如对字符位置进行加权处理。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "science39",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.18"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
